package com.ticket.sass.common.ticketserver.platform.cx300

import com.google.gson.Gson
import com.google.gson.JsonSyntaxException
import com.ticket.sass.common.ticketserver.configure.ConfigData
import com.ticket.sass.common.ticketserver.exceptions.ParseDataException
import com.ticket.sass.common.ticketserver.parseJson
import com.ticket.sass.common.ticketserver.platform.fhyz.RequestClient
import mu.KotlinLogging
import okhttp3.FormBody
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Request
import org.apache.axis.client.Call
import org.apache.axis.client.Service
import org.apache.axis.encoding.XMLType
import org.json.XML
import okio.Buffer
import java.nio.charset.Charset
import java.time.Duration
import java.time.LocalDateTime
import javax.xml.namespace.QName
import javax.xml.rpc.ParameterMode

object RequestClient {
    private val logger = KotlinLogging.logger {}

    data class ResultModel(
        val xmlns: String,
        val content: String,
    )

    private val okHttpClient = OkHttpClient.Builder()
        .addInterceptor(Interceptor { chain ->
            val request = chain.request()

            val requestBody = request.body?.let {
                val requestBodyBuffer = Buffer()
                it.writeTo(requestBodyBuffer)
                requestBodyBuffer.readString(Charset.forName("UTF-8"))
            }

            val beginTime = LocalDateTime.now()
            val response = chain.proceed(request)

            val responseBody = response.body?.let {
                it.source().request(Long.MAX_VALUE)
                it.source().buffer.clone().readString(Charset.forName("UTF-8"))
            }

            val endTime = LocalDateTime.now()
            val between = Duration.between(beginTime, endTime)
            val millis = between.toMillis()

            logger.info("请求时间：${millis}毫秒, 请求地址：${request.url}，请求参数：${requestBody}，响应状态码：${response.code}，响应数据：${responseBody}")

            response
        })
        .build()

    private val service = Service()

    data class Paramter(
        val name: String,
        val type: QName,
        val value: String,
    )

    fun sendXml(api: String, input: List<Paramter>): String {
        val call = service.createCall().apply {
            targetEndpointAddress = Configurer.ENDPOINT_TICKET
            operationName = QName(Configurer.NAMESPACE_TICKET, api)

            input.forEach { addParameter(it.name, it.type, ParameterMode.IN) }

            returnType = XMLType.XSD_STRING
        }
        val params = input.map { it.value }

        return wrapInvoke(call, params)
    }

    fun send(api: String, input: List<Paramter>, configData: ConfigData.CX300): String {
        val call = service.createCall().apply {
            this as Call
            targetEndpointAddress = Configurer.ENDPOINT_TICKET
            operationName = QName(Configurer.NAMESPACE_TICKET, api)
            soapActionURI = Configurer.NAMESPACE_TICKET + api

            input.forEach {
                addParameter(it.name, it.type, ParameterMode.IN)
            }
            addParameter("pVerifyInfo", XMLType.XSD_STRING, ParameterMode.IN)

            returnType = XMLType.XSD_STRING
        }

        val params = mutableListOf<String>()
        params.addAll(input.map { it.value })   // 业务参数 - 动态
        params.add(Configurer.generateTicketSign(configData, input.map { it.value }))   // 固定参数 pVerifyInfo

        println("params ===== $params")

        return wrapInvoke(call, params)
    }

    fun send2(
        api: String,
        params: List<Map<String, Any?>>,
        configData: ConfigData.CX300,
        excludeVerifyFields: List<String> = listOf()
    ): String {
        val requestBody = FormBody.Builder().let { bodyBuilder ->
            params.forEach {
                bodyBuilder.add(it.keys.first(), it.values.first().toString())
            }
            bodyBuilder.add(
                "pVerifyInfo",
                Configurer.generateTicketSign(
                    configData,
                    params.filter { it.keys.first() !in excludeVerifyFields }.map { it.values.first().toString() }
                )
            )
            bodyBuilder.build()
        }

        val request = Request.Builder()
            .url(Configurer.ENDPOINT_TICKET + "/" + api)
            .post(requestBody)
            .build()

        val response = okHttpClient.newCall(request).execute()

        return response.body?.string()?.let {
            XML.toJSONObject(it, true).getJSONObject("string").toString(0).parseJson(ResultModel::class.java).content
        } ?: error("接口无数据响应")
    }

    /**
     * 封装 rpc 调用
     *
     * @param call      rpc客户端
     * @param params    调用参数
     *
     * @return [String]
     */
    private fun wrapInvoke(call: javax.xml.rpc.Call, params: List<String>): String {
        val beginTime = LocalDateTime.now()
        val res = call.invoke(params.toTypedArray()) as String
        val between = Duration.between(beginTime, LocalDateTime.now())

        println("pVerifyInfo ===== ${call.getParameterTypeByName("pVerifyInfo")}")

        logger.info { "请求时间：${between.toMillis()}毫秒, 请求地址：${call.operationName}，请求参数：${params.map { it }}，响应数据：${res}" }

        return XML.toJSONObject(res, true).toString(0)
    }
}